package net.simflow.swf.analyzer.network.data;

import java.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;

import net.simflow.swf.analyzer.exec.data.ServiceOverAbilityRecord;

public class Flow implements ElementInNetwork {
	private String name;
	private String nameInNetwork;
	private URI uri;
	private HashSet<Node> entranceSet = new HashSet<Node>();
	private HashSet<Node> exitSet = new HashSet<Node>();
	private HashSet<Node> nodeSet = new HashSet<Node>();
	private HashSet<Service> serviceSet = new HashSet<Service>();

	public URI getUri() {
		return uri;
	}

	private HashSet<Instance> instanceSet = new HashSet<Instance>();

	public Flow(String name, URI uri) {
		this.name = name;
		this.uri = uri;
	}

	public boolean addEntrance(Node entrance) {
		return this.entranceSet.add(entrance);
	}

	public boolean removeEntrance(Node entrance) {
		return this.entranceSet.remove(entrance);
	}

	public boolean addExit(Node exit) {
		return this.exitSet.add(exit);
	}

	public boolean removeExit(Node exit) {
		return this.exitSet.remove(exit);
	}

	public boolean addNode(Node node) {
		return this.nodeSet.add(node);
	}

	public boolean addNodeSet(Collection<? extends Node> nodeSet) {
		return this.nodeSet.addAll(nodeSet);
	}

	public void removeNode(Node node) {
		this.nodeSet.remove(node);
	}

	public boolean addService(Service service) {
		return this.serviceSet.add(service);
	}

	public boolean addServiceSet(Collection<? extends Service> ServiceSet) {
		return this.serviceSet.addAll(ServiceSet);
	}

	public boolean addInstance(Instance core) {
		return this.instanceSet.add(core);
	}

	public boolean addInstanceSet(Collection<? extends Instance> InstanceSet) {
		return this.instanceSet.addAll(InstanceSet);
	}

	public String getName() {
		return name;
	}

	public String getNameInNetwork() {
		return nameInNetwork;
	}

	public void setNameInNetwork(String name) {
		nameInNetwork = name;
	}

	public URI getURI() {
		return uri;
	}

	public HashSet<Node> getEntranceSet() {
		return entranceSet;
	}

	public HashSet<Node> getExitSet() {
		return exitSet;
	}

	public HashSet<Node> getNodeSet() {
		return nodeSet;
	}

	public HashSet<Service> getServiceSet() {
		return serviceSet;
	}

	public HashSet<Instance> getInstanceSet() {
		return instanceSet;
	}

	public HashSet<Link> getDataLinkSet() {
		HashSet<Link> set = new HashSet<Link>();

		for (Node s : nodeSet) {
			for (Node r : s.getChildDataSet()) {
				set.add(new Link(s, r, false));
			}
		}

		return set;
	}

	public HashSet<Link> getControlLinkSet() {
		HashSet<Link> set = new HashSet<Link>();

		for (Node s : nodeSet) {
			for (Node r : s.getChildControlSet()) {
				set.add(new Link(s, r, true));
			}
		}

		return set;
	}

	public HashSet<Link> getLinkSet() {
		HashSet<Link> set = new HashSet<Link>();
		set.addAll(this.getDataLinkSet());
		set.addAll(this.getControlLinkSet());
		return set;
	}

	@Override
	public int hashCode() {
		return this.uri.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}

		if (obj == null) {
			return false;
		}

		Flow flow;

		try {
			flow = (Flow) obj;
		} catch (Exception e) {
			return false;
		}

		return this.uri.equals(flow.getURI());
	}

	public String getShortInfo() {
		StringBuffer str = new StringBuffer();
		str.append(this.getNameInNetwork());
		str.append("{");
		str.append("Node(" + this.getNodeSet().size() + ");");
		str.append("Service(" + this.getServiceSet().size() + ")");
		str.append("}");

		return str.toString();
	}

	public String getFullInfo() {
		StringBuffer str = new StringBuffer();

		str.append(this.getNameInNetwork());
		str.append("{");
		str.append("Nodes(" + this.getNodeSet().size() + ")");
		str.append("[");
		for (Node n : this.getNodeSet()) {
			str.append(n.getFullInfo()).append(",");
		}
		str.deleteCharAt(str.length() - 1);
		str.append("]");
		str.append("Services(" + this.getServiceSet().size() + ")");
		str.append("[");
		for (Service s : this.getServiceSet()) {
			str.append(s.getFullInfo()).append(",");
		}
		str.deleteCharAt(str.length() - 1);
		str.append("]");
		str.append("}");

		return str.toString();
	}

	@Override
	public String toString() {
		return getFullInfo();
	}

	private HashSet<Integer> taskIdList = new HashSet<Integer>();
	private HashSet<Integer> mishandledTaskList = new HashSet<Integer>();
	// private HashSet<Integer> totalIdList = new HashSet<Integer>();
	// private HashSet<Integer> execIdList = new HashSet<Integer>();
	// private HashSet<Integer> unfinishedIdList = new HashSet<Integer>();
	private ServiceOverAbilityRecord serviceOverAbilityRecord;
	private int flowRelDeadline;

	public void initExec(HashMap<Service, Integer> serviceAbilityMap,
			HashMap<Service, Integer> serviceSchduleMap, int totalStep,
			int flowRelDeadline) {
		this.taskIdList.clear();
		this.mishandledTaskList.clear();
		// this.totalIdList.clear();
		// this.execIdList.clear();
		// this.unfinishedIdList.clear();
		this.serviceOverAbilityRecord = null;
		this.flowRelDeadline = flowRelDeadline;
		for (Node n : this.getNodeSet()) {
			n.initExec(serviceAbilityMap.get(n.getService()), totalStep, this,
					serviceSchduleMap.get(n.getService()));
		}
	}

	public void newTask(int taskId, int time) {
		// this.totalIdList.add(taskId);
		// this.unfinishedIdList.add(taskId);
		this.taskIdList.add(taskId);
		for (Node n : this.getEntranceSet()) {
			n.addTask(taskId, null, false, time, time
					+ this.flowRelDeadline);
			n.submitPreparedTasks(time);
		}
	}

	public HashSet<Integer> nextStep(int time) {
		if (taskIdList.isEmpty()) {
			return new HashSet<Integer>();
		}

		for (Node n : this.getNodeSet()) {
			n.getService().nextStep(time);
		}

		for (Node n : this.getNodeSet()) {
			n.submitPreparedTasks(time);
		}

		taskIdList.removeAll(this.mishandledTaskList);
		HashSet<Integer> successset = new HashSet<Integer>(taskIdList);
		for (Node n : this.getNodeSet()) {
			successset.removeAll(n.getService().getTaskIdList());
			if (successset.isEmpty()) {
				break;
			}
		}

		this.mishandledTaskList.clear();

		if (!successset.isEmpty()) {
			this.taskIdList.removeAll(successset);
			// this.unfinishedIdList.remove(successset);
			// this.execIdList.addAll(successset);
			for (Node n : this.getNodeSet()) {
				n.removeAllTask(successset);
			}
		}

		return successset;

	}

	public void returnMishandledTask(int taskId, int time, Service svc) {
		if (serviceOverAbilityRecord == null) {
			serviceOverAbilityRecord = new ServiceOverAbilityRecord();
			serviceOverAbilityRecord.setService(svc);
			serviceOverAbilityRecord.setTaskId(taskId);
			serviceOverAbilityRecord.setTime(time);
		}
		this.mishandledTaskList.add(taskId);
	}

	// public HashSet<Integer> getTotalIdList() {
	// return totalIdList;
	// }
	//
	// public HashSet<Integer> getExecIdList() {
	// return execIdList;
	// }
	//
	// public HashSet<Integer> getUnfinishedIdList() {
	// return unfinishedIdList;
	// }

	public ServiceOverAbilityRecord getServiceOverAbilityRecord() {
		return this.serviceOverAbilityRecord;
	}
}
